﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.ServiceModel;
using System.Text;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Net;
using System.Drawing;
using System.Net.Sockets;
using System.Collections.ObjectModel;
using DirectShowLib;
using RemotingMessage;
using UDP_Lib;
using Voice;
using Utilities;
using SelectorDevice;
using System.Threading;
using System.Reflection;
using System.Windows;
using WPFMediaKit.DirectShow.Controls;

namespace VideoChatClient
{
    //[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class VideoChatClientViewModel : INotifyPropertyChanged, IConference
    {
        private delegate void NoArgDelegate();

        #region Members
        private InstanceContext instanceContext;
        private IOnlineStatus ostat = null;
        private DuplexChannelFactory<IConferenceChannel> factory;
        private DispatcherTimer tmrSendVideo = new DispatcherTimer();
        private WaveFormat waveFormat = new WaveFormat(11025, 16, 2);
    //    private FifoStream m_Fifo = new FifoStream();
        private byte[] m_PlayBuffer;
        public DeviceExch DeviceControl = new DeviceExch();
        #endregion
        
        #region Properties
        
        public string ServerName { get { return Properties.Settings.Default.ServerName;}}
        public string MachineName { get { return Environment.MachineName;}}
        public string OSVersion { get { return Environment.OSVersion.VersionString; } }

        public UDP_Server UDP_VIDEO_Server;
        public UDP_Server UDP_AUDIO_Server;

        public UDP_Server UDP_VIDEO_Conference_Server;
        public UDP_Server UDP_AUDIO_Conference_Server;

        public UDP_Client UDP_VIDEO_Conference_Client;
        public UDP_Client UDP_AUDIO_Conference_Client;

        #region SoftWareVersion
        private string softWareVersion = "";
        public string SoftWareVersion
        {
            get { return softWareVersion; }
            set { softWareVersion = value; OnPropertyChanged("SoftWareVersion"); }
        }
        #endregion

        #region TestInfo
        private string testInfo;
        public string TestInfo
        {
            get { return testInfo; }
            private set
            {
                testInfo = value;
                OnPropertyChanged("TestInfo");
            }
        }
        #endregion

        #region TestInfo2
        private string testInfo2;
        public string TestInfo2
        {
            get
            {
                return "Video:" + MissVideoMessage + " Audio:" + MissAudioMessage + " Delta:" + ServiceTime;
            }
            private set
            {
                testInfo2 = value;
                OnPropertyChanged("TestInfo2");
            }
        }
        #endregion

        #region TestInfo3
        private string testInfo3;
        public string TestInfo3
        {
            get { return testInfo3; }
            private set
            {
                testInfo3 = value;
                OnPropertyChanged("TestInfo3");
            }
        }
        #endregion

        #region ClickOnceUpdater
        private ClickOnceExt clickOnceUpdater = new ClickOnceExt() 
        { 
            CheckUpdateInterval = Properties.Settings.Default.CheckUpdateInterval,
            IsRestartWhenUpdate = true,
            IsAutoUpdate = Properties.Settings.Default.IsAutoUpdate
        };
        public ClickOnceExt ClickOnceUpdater
        {
            get { return clickOnceUpdater; }
            private set
            {
                if (clickOnceUpdater == value) return;
                clickOnceUpdater = value;
                clickOnceUpdater.Restarting += (_1, _2) => { CloseModel(); };
                OnPropertyChanged("ClickOnceUpdater");
            }
        }
        #endregion

        #region IsAutoStartEnable
        public bool IsAutoStartEnable
        {
            get { return Properties.Settings.Default.IsAutoStartEnable; }
            set
            {
                Properties.Settings.Default.IsAutoStartEnable = value;
                CheckAutoStart();
                OnPropertyChanged("IsAutoStartEnable");
            }
        }
        #endregion

        #region IsAutoUpdate
        public bool IsAutoUpdate
        {
            get { return Properties.Settings.Default.IsAutoUpdate; }
            set
            {
                Properties.Settings.Default.IsAutoUpdate = value;
                ClickOnceUpdater.IsAutoUpdate = value;
                OnPropertyChanged("IsUpdateModeOn");
            }
        }
        #endregion

        #region IsUDPConference
        public bool IsUDPConference
        {
            get { return Properties.Settings.Default.IsUDPConference; }
            set
            {

                Properties.Settings.Default.IsUDPConference = value;
                OnPropertyChanged("IsUDPConference");
            }
        }
        #endregion

        #region IsShowIPVideo
        public bool IsShowIPVideo
        {
            get { return Properties.Settings.Default.IsShowIPVideo; }
            set
            {
                Properties.Settings.Default.IsShowIPVideo = value;
                OnPropertyChanged("_IsShowIPVideo");
            }
        }
        #endregion

        #region FPS_Video
        private string _FPS_Video;
        public string FPS_Video
        {
            get { return _FPS_Video; }
            set
            {
                _FPS_Video = value;
                OnPropertyChanged("FPS_Video");
            }
        }
        #endregion

        #region CheckUpdateInterval
        public TimeSpan CheckUpdateInterval
        {
            get { return Properties.Settings.Default.CheckUpdateInterval; }
            set
            {
                Properties.Settings.Default.CheckUpdateInterval = value;
                ClickOnceUpdater.CheckUpdateInterval = value;
                OnPropertyChanged("CheckUpdateInterval");
            }
        }
        #endregion

        private DsDevice[] _webCamDevices;

        public DsDevice[] WebCamDevices
        {
            get { return _webCamDevices; }
            set
            {
                _webCamDevices = value;
                 OnPropertyChanged("WebCamDevices"); 
            }
        }

        private DsDevice _curCam;
        public DsDevice CurCam
        {
            get { return _curCam; }
            set
            {
                _curCam = value;
                OnPropertyChanged("CurCam"); 
            }
        }
        
        public class ConferenceClass: INotifyPropertyChanged
        {
            private string _sName;
            public string SName
            {
                get { return _sName; }
                set
                {
                    _sName = value;
                    OnPropertyChanged("SName");
                }
            }

            public enum State
            {
                Initial = 0,
                CanAnswer = 1,
                Connecting = 2,
                Work =3
            }

            private string _whoTalk;
            public string WhoTalk
            {
                get { return _whoTalk; }
                set
                {
                    _whoTalk = value;
                    OnPropertyChanged("WhoTalk");
                }
            }

            private bool _isAnswer;
            public bool IsAnswer
            {
                get { return _isAnswer; }
                set
                {
                    _isAnswer = value;
                    OnPropertyChanged("IsAnswer");
                }
            }

            private bool _isNotAnswer;
            public bool IsNotAnswer
            {
                get { return _isNotAnswer; }
                set { 
                    _isNotAnswer = value;
                    OnPropertyChanged("IsNotAnswer");
                }
            }

            private State _curState = State.Initial;
            public State CurState
            {
                get { return _curState; }
                set
                {
                    _curState = value;
                    switch (_curState)
                    {
                        case State.Initial:
                            VisConference = Visibility.Collapsed;
                            VisCall = Visibility.Visible;
                            VisAnswer = Visibility.Collapsed;
                            NotAnswerCall = Visibility.Collapsed;
                         /*   
                         IsNotAnswer = false;
                            IsAnswer = false;
                            ReqConnect = false;//*/
                            break;
                        case State.Work:
                            VisConference = Visibility.Visible;
                            VisCall = Visibility.Collapsed;
                            VisAnswer = Visibility.Collapsed;
                            NotAnswerCall = Visibility.Visible;
                         /*   
                         IsNotAnswer = false;
                            IsAnswer = false;
                            ReqConnect = false;//*/
                            break;
                        case State.Connecting:
                            VisCall = Visibility.Collapsed;
                            VisAnswer = Visibility.Collapsed;
                            NotAnswerCall = Visibility.Visible;
                            VisConference = Visibility.Visible;
                          /*  
                          IsNotAnswer = false;
                            IsAnswer = false;
                            ReqConnect = false;//*/
                            break;
                        case State.CanAnswer:
                            VisConference = Visibility.Collapsed;
                            VisCall = Visibility.Collapsed;
                            VisAnswer = Visibility.Visible;
                            NotAnswerCall = Visibility.Visible;
                         /*   
                           IsNotAnswer = false;
                            IsAnswer = false;
                            ReqConnect = false;//*/
                            break;

                    }
                    OnPropertyChanged("CurState");
                }
            }

            private BitmapImage _conferenceImage;
            public BitmapImage ConferenceImage
            {
                get { return _conferenceImage; }
                set 
                { 
                    _conferenceImage = value;
                    OnPropertyChanged("ConferenceImage");
                }
            }

            private bool _reqConnect;
            public bool ReqConnect
            {
                get { return _reqConnect; }
                set
                {
                    _reqConnect = value;
                    CurState = State.Connecting;
                    OnPropertyChanged("ReqConnect");
                }
            }

            private Visibility _visConference;
            public Visibility VisConference
            {
                get { return _visConference; }
                set
                {
                    _visConference = value;
                    OnPropertyChanged("VisConference");
                }
            }

            private Visibility _visAnswer = Visibility.Collapsed;
            public Visibility VisAnswer
            {
                get { return _visAnswer; }
                set
                {
                    _visAnswer = value;
                    OnPropertyChanged("VisAnswer");
                }
            }

            private Visibility _visCall = Visibility.Visible;
            public Visibility VisCall
            {
                get { return _visCall; }
                set
                {
                    if (_visCall == value) return;
                    _visCall = value;
                    OnPropertyChanged("VisCall");
                }
            }

            private Visibility _NotAnswerCall=Visibility.Collapsed;
            public Visibility NotAnswerCall
            {
                get { return _NotAnswerCall; }
                set
                {
                    _NotAnswerCall = value;
                    OnPropertyChanged("NotAnswerCall");
                }
            }

            
            #region INotifyPropertyChanged Members
            public event PropertyChangedEventHandler PropertyChanged = delegate { };
            private void OnPropertyChanged(string propertyName)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
            #endregion INotifyPropertyChanged Members
        }

        private ObservableCollection<ConferenceClass> _conferenceRoom;
        public ObservableCollection<ConferenceClass> ConferenceRoom
        {
            get { return _conferenceRoom; }
            set { 
                _conferenceRoom = value;
                OnPropertyChanged("ConferenceRoom");
            }
        }
        
        #region Room
        public string Room
        {
            get { return Properties.Settings.Default.Room; }
            set { Properties.Settings.Default.Room = value; OnPropertyChanged("Room"); }
        }
        #endregion

        #region ClientType
        public ClientTypes ClientType
        {
            get { return Properties.Settings.Default.ClientType; }
            set { Properties.Settings.Default.ClientType = value; OnPropertyChanged("ClientType"); }
        }
        #endregion

        #region MainDispatcher
        private Dispatcher mainDispatcher = Dispatcher.CurrentDispatcher;
        public Dispatcher MainDispatcher { get { return mainDispatcher; } }
        #endregion

        #region IpAddres
        private string strIpAddres;
        public string IpAddres
        {
            get { return strIpAddres; }
            private set
            {
                if (strIpAddres == value) return;
                strIpAddres = value;
                OnPropertyChanged("IpAddres");
            }
        }
        #endregion
        
        #region ClientList
        private ObservableCollection<ClientInfo> clientList = new ObservableCollection<ClientInfo>();
        public ObservableCollection<ClientInfo> ClientList
        {
            get { return clientList; }
            private set
            {
                clientList = value;
                OnPropertyChanged("ClientList");
            }
        }
        #endregion
       
        #region IsConnect
        private bool bIsConnect = false;
        public bool IsConnect
        {
            get { return bIsConnect; }
            private set
            {
                if (bIsConnect == value) return;
                bIsConnect = value;
                OnPropertyChanged("IsConnect");
            }
        }
        #endregion
       
        #region IpVideoSample
        private BitmapImage bmpIpVideoSample;
        public BitmapImage IpVideoSample
        {
            get { return bmpIpVideoSample; }
            set
            {
                bmpIpVideoSample = null;
                bmpIpVideoSample = value;
                OnPropertyChanged("IpVideoSample");
            }
        }
        #endregion

        #region MyVideoFrame
        private byte[] myVideoFrame;
        public byte[] MyVideoFrame
        {
            get { return myVideoFrame; }
            set
            {
                if (myVideoFrame == value) return;
                myVideoFrame = value;
                OnPropertyChanged("MyVideoFrame");
            }
        }
        #endregion
        
        #region Participant
        private IConferenceChannel participant;
        public IConferenceChannel Participant
        {
            get { return participant; }
        }
        #endregion

        #region IsIpVideoRecived
        private bool bIsIpVideoRecived = true;
        public bool IsIpVideoRecived
        {
            get { return bIsIpVideoRecived; }
            set
            {
                if (bIsIpVideoRecived == value) return;
                bIsIpVideoRecived = value;
                OnPropertyChanged("IsIpVideoRecived");
            }
        }
        #endregion

        #region IsIpAudioRecived
        private bool bIsIpAudioRecived = true;
        public bool IsIpAudioRecived
        {
            get { return bIsIpAudioRecived; }
            set
            {
                if (bIsIpAudioRecived == value) return;
                bIsIpAudioRecived = value;
                OnPropertyChanged("IsIpAudioRecived");
            }
        }
        #endregion
       
        #region SendVideoInerval
        public TimeSpan SendVideoInerval
        {
            get { return Properties.Settings.Default.SendVideoInerval; }
            set { Properties.Settings.Default.SendVideoInerval = value; OnPropertyChanged("SendVideoInerval"); }
        }
        #endregion

        #region ServiceDeltaTime
        private TimeSpan serviceDeltaTime = TimeSpan.FromMilliseconds(0);
        public TimeSpan ServiceDeltaTime
        {
            get { return serviceDeltaTime; }
            set
            {
                if (serviceDeltaTime == value) return;
                serviceDeltaTime = value;
                OnPropertyChanged("ServiceDeltaTime");
                OnPropertyChanged("ServiceTime");
            }
        }
        #endregion

        #region ServiceTime
        public DateTime ServiceTime
        {
            get { return DateTime.Now - ServiceDeltaTime; }
        }
        #endregion

        #region WaveIn
        private WaveInRecorder waveIn;
        public WaveInRecorder WaveIn
        {
            get { return waveIn; }
            private set
            {
                if (waveIn == value) return;
                waveIn = value;
                waveIn.WaveRecorded += (_1, e) => 
                {
                    if (Participant == null) return;
               //     e.Date = ServiceTime;
                    var t = SelectedConferenceRoom.Concat(SelectedSelectorRooms).ToList();
                    if (t.Count() > 0 && Participant.State == CommunicationState.Opened)
                    {
                        if(IsUDPConference==false)
                            Participant.AudioSample(new SendBytes(){Data = VoiceCapturedEventArgs.Serialize(e),From = Room,To = t});
                        else
                        {
                            var um = new UPD_Message()
                                    {
                                        Data = VoiceCapturedEventArgs.Serialize(e),
                                        From = Room,
                                        To = t
                                    };
                            UDP_AUDIO_Conference_Client.SendBigBuffer(um.ToByte());
                        }
                    } 
                };
                OnPropertyChanged("WaveIn");
            }
        }
        #endregion

        #region WaveOut
        private WaveOutPlayer waveOut;
        public WaveOutPlayer WaveOut
        {
            get 
            {
                return waveOut; 
            }
            set
            {
                if (waveOut == value) return;
                waveOut = value;
                OnPropertyChanged("WaveOut");
            }
        }
        #endregion

        #region ControlDeviceStatus

        private int NumSendLight;
        private DeviceExch.DevStatus deviceControlStatus = new DeviceExch.DevStatus();
        public DeviceExch.DevStatus DeviceControlStatus
        {
            get { return deviceControlStatus; }
            set
            {
                if (deviceControlStatus == value) return;
                if (deviceControlStatus==null) return;
                deviceControlStatus = value;
                //Это здесь не нужно?!
                // ты Передаёшь на устройство те лампочки которые ты с него получил
               // DeviceControl.TaskCommand(SelectorDevice.DeviceExch.CommandName.LightSend, deviceControlStatus.LightStatus);
                OnPropertyChanged("DeviceControlStatus");

                NumSendLight++;
                if (NumSendLight<8) return;
                NumSendLight = 0;
                if (Participant != null && Participant.State == CommunicationState.Opened)
                {
                    Participant.DeviceControlLights(Room, deviceControlStatus.LightStatus);
                }
            }
        }
        #endregion

        #region SelectedSelectorRooms
        private List<string> selectedSelectorRooms = new List<string>();
        public List<string> SelectedSelectorRooms
        {
            get { return selectedSelectorRooms; }
            set
            {
                if (selectedSelectorRooms == value) return;
                selectedSelectorRooms = value;
                OnPropertyChanged("SelectedSelectorRooms");
            }
        }
        #endregion

        #region SelectedConferenceRoom
        private List<string> selectedConferenceRoom = new List<string>();
        public List<string> SelectedConferenceRoom
        {
            get { return selectedConferenceRoom; }
            set
            {
                if (selectedConferenceRoom == value) return;
                selectedConferenceRoom = value;
                OnPropertyChanged("SelectedConferenceRoom");
            }
        }
        #endregion
        
        #endregion //Properties

        #region Methods
        public VideoChatClientViewModel()
        {
            try
            {
                UDP_VIDEO_Server = new UDP_Server() { ServerIP = "224.0.0.1", ServerPort = 5000 };
                UDP_VIDEO_Server.evRecBuf += (CurUDP_Server_evRecBuf);
                UDP_VIDEO_Server.Start();

                UDP_AUDIO_Server = new UDP_Server() { ServerIP = "224.0.0.1", ServerPort = 6000 };
                UDP_AUDIO_Server.evRecBuf += (UDP_AUDIO_Server_evRecBuf);
                UDP_AUDIO_Server.Start();
                /*
                UDP_VIDEO_Conference_Client = new UDP_Client() { ServerIP = "224.0.0.1", ServerPort = 7000 };
                UDP_VIDEO_Conference_Client.Start();

                UDP_VIDEO_Conference_Server = new UDP_Server() { ServerIP = "224.0.0.1", ServerPort = 7000 };
                UDP_VIDEO_Conference_Server.evRecBuf += (UDP_VIDEO_Conference_Server_evRecBuf);
                UDP_VIDEO_Conference_Server.Start();

                UDP_AUDIO_Conference_Client = new UDP_Client() { ServerIP = "224.0.0.1", ServerPort = 8000 };
                UDP_AUDIO_Conference_Client.Start();

                UDP_AUDIO_Conference_Server = new UDP_Server() { ServerIP = "224.0.0.1", ServerPort = 8000 };
                UDP_AUDIO_Conference_Server.evRecBuf += (UDP_AUDIO_Conference_Server_evRecBuf);
                UDP_AUDIO_Conference_Server.Start();*/
            }
            catch (Exception ex)
            {
                MessageBox.Show("Ошибка создания UDP серверов\n" + ex.ToString());
            }

            ConferenceRoom = new ObservableCollection<ConferenceClass>();

            for(int i=0;i<4;i++)
            {
                string sRoom = MachineSettings.ButtonNumberToRoom(31 + i);

                var cc = new ConferenceClass() { SName = sRoom };
                cc.PropertyChanged += VideoChatClientViewModel_PropertyChanged;
                ConferenceRoom.Add(cc);
            }

            WebCamDevices = MultimediaUtil.VideoInputDevices;
            RunConferencService();

            if (waveOut == null) 
                waveOut = new WaveOutPlayer(-1, waveFormat, 4096, 2, Filler);
            if (WaveIn == null)
                WaveIn = new WaveInRecorder(-1, waveFormat, 4096, 2);

            tmrSendVideo.Interval = SendVideoInerval;
            tmrSendVideo.Tick += (_1,_2) => 
            {
                if (Participant == null 
                    || MyVideoFrame == null 
                    || SelectedConferenceRoom.Count == 0) return;

                tmrSendVideo.Stop();
                if (IsUDPConference==false)
                {
                    participant.VideoSample(
                        new SendBytes
                            {
                                From = Room,
                                Data = MyVideoFrame.Clone() as byte[],
                                To = SelectedConferenceRoom
                            });
                }
                else
                {
                    var um = 
                        new UPD_Message()
                            {
                                From = Room,
                                Data = MyVideoFrame.Clone() as byte[],
                                To = SelectedConferenceRoom
                            };
                    UDP_VIDEO_Conference_Client.SendBigBuffer(um.ToByte());
                }
                tmrSendVideo.Start();
            };
            tmrSendVideo.Start();
            InitControlDevice();
            CheckAutoStart();

        }

        void UDP_AUDIO_Conference_Server_evRecBuf(byte[] bByf)
        {
            UPD_Message sb = UPD_Message.FromByte(bByf);
            if (sb.From == Room) return;
            if (sb.To.Contains(Room) == false && sb.From != "server") return;

            var AudioSample = VoiceCapturedEventArgs.Deserialise(sb.Data.Clone() as byte[]);

            GC.Collect();

            if (sb.From == ServerName)
            {
                if (!IsIpAudioRecived) return;
            }
            lock (this)
            {
                this.MainDispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                (ThreadStart)delegate()
                {
                    if (sb.From != null)
                    {
                        string str = sb.To.Aggregate("", (current, var) => current + (var + " "));
                        //TestInfo2 = "thisR =" + Room + "    audio R:" + From + "  To " + str;
                    }
                    else
                    {
                        // TestInfo2 = "";
                    }
                });

                if (bFlagMixVoice)
                {
                    bool bFlag = false;

                    foreach (ClassMultiAudio t in lst_bufSampAudio.Where(t => t.from == sb.From))
                    {
                        if (t.IndexMessage > AudioSample.IndexMessage)
                        {
                            continue;
                        }
                        t.DtRec = DateTime.Now;
                        t.IndexMessage = AudioSample.IndexMessage;
                        t.AudioStream.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                        bFlag = true;
                    }

                    if (bFlag == false)
                    {
                        var cl = new ClassMultiAudio()
                        {
                            from = sb.From,
                            IndexMessage = AudioSample.IndexMessage,
                            DtRec = DateTime.Now
                        };
                        cl.AudioStream.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                        lst_bufSampAudio.Add(cl);

                    }
                }
                else mFifo.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                RecByte += AudioSample.Source.Length;


            }
            //throw new NotImplementedException();
        }

        void UDP_VIDEO_Conference_Server_evRecBuf(byte[] bByf)
        {
            UPD_Message sb = UPD_Message.FromByte(bByf);
            if (sb.From == Room || !sb.To.Contains(Room) || !ContainsConferenceRoom(sb.From)) return;

            //   byte[] btVideo = btVideoSample.Clone() as byte[];

            MainDispatcher.BeginInvoke((Action<byte[]>)((btVideo) =>
            {
                MemoryStream ms = new MemoryStream(btVideo);
                BitmapImage bmp = new BitmapImage();
                bmp.BeginInit();
                bmp.StreamSource = ms;
                bmp.EndInit();

                foreach (var item in ConferenceRoom.Where(p => p.SName == sb.From))
                {
                    ConferenceClass item1 = item;

                    if (item1 != null)
                    {
                        MainDispatcher.BeginInvoke((Action<BitmapImage>)((t) => { item1.ConferenceImage = t; }), bmp);
                    }
                }
            }),
            DispatcherPriority.Send, sb.Data.Clone() as byte[]);
            //throw new NotImplementedException();
        }

        void UDP_AUDIO_Server_evRecBuf(byte[] bByf)
        {
            GC.Collect();
            var AudioSample = VoiceCapturedEventArgs.Deserialise(bByf.Clone() as byte[]);

            lock (lst_bufSampAudio)
            {
                if (bFlagMixVoice)
                {
                    bool bFlag = false;
                    foreach (ClassMultiAudio t in lst_bufSampAudio.Where(t => t.from == "server"))
                    {
                        if (t.IndexMessage > AudioSample.IndexMessage)
                        {
                            continue;
                        }

                        t.IndexMessage = AudioSample.IndexMessage;
                        t.DtRec = DateTime.Now;
                        t.AudioStream.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                        bFlag = true;
                    }

                    if (bFlag == false)
                    {
                        var cl = new ClassMultiAudio
                                     {
                                         IndexMessage = AudioSample.IndexMessage,
                                         from = "server",
                                         DtRec = DateTime.Now
                                     };
                        cl.AudioStream.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                        lst_bufSampAudio.Add(cl);

                    }
                }
                else mFifo.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                RecByte += AudioSample.Source.Length;
            }
        }

        void GetIpVideo(byte[] bByf)
        {
            numFrame++;
            if ((DateTime.Now - DT_VideoFpsCalc).TotalMilliseconds > 1000)
            {
                FPS_Video = numFrame.ToString() + " fps";
                DT_VideoFpsCalc = DateTime.Now;
                numFrame = 0;
            }
            if (IsShowIPVideo == false) return;

            var ms = new MemoryStream(bByf);
            var bmp = new BitmapImage();
            bmp.BeginInit();
            bmp.StreamSource = ms;
            bmp.EndInit();
            IpVideoSample = bmp;
        }

        void CurUDP_Server_evRecBuf(byte[] bByf)
        {
            MainDispatcher.Invoke((Action<byte[]>)(GetIpVideo), DispatcherPriority.Render, bByf.Clone() as byte[]);
            //throw new NotImplementedException();
        }

        void VideoChatClientViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var cc = ((ConferenceClass) sender);
            switch(e.PropertyName)
            {
                case "CurState":
                    {
                        var BufB = new byte[4];
                        for (int i = 0; i < 4; i++)
                            BufB[i] = (byte)ConferenceRoom[i].CurState;
                        DeviceControl.TaskCommand(SelectorDevice.DeviceExch.CommandName.ConfStatus, BufB);
                    }
                    break;
                case "ReqConnect":
                    if (cc.ReqConnect == true)
                    {
                        if (Participant != null && Participant.State == CommunicationState.Opened)
                            Participant.BeginCall(Room, cc.SName);
                    }
                    break;
                case "IsAnswer":
                    if (cc.IsAnswer == true)
                    {
                        string Who = cc.WhoTalk;

                        if (SelectedConferenceRoom.Contains(Who) == false)
                        {
                            SelectedConferenceRoom.Add(Who);
                        }

                        if(Who==null)
                        {
                            MessageBox.Show("WHO err " + cc.SName + "   " + Room);
                        }

                        if (Participant != null && Participant.State == CommunicationState.Opened)
                            Participant.StartConference(Room, Who);
                        cc.CurState = ConferenceClass.State.Work;
                    }
                    break;
                case "IsNotAnswer":
                    if (cc.IsNotAnswer == true)
                    {
                        string str = cc.WhoTalk;
                        if (SelectedConferenceRoom.Contains(str))
                            SelectedConferenceRoom.Remove(str);

                        if (Participant != null && Participant.State == CommunicationState.Opened)
                        {
                            Participant.StopConference(Room, cc.SName);
                            Participant.StopConference(Room, str);
                        }

                        cc.CurState = ConferenceClass.State.Initial;
                    }
                    break;
            }
            //throw new NotImplementedException();
        }
        public void CloseModel()
        {
            try
            {
                DeviceControl.EndWrk();
            }
            catch (Exception)
            {
                
            }
            try
            {
                UDP_VIDEO_Server.Stop();
                UDP_AUDIO_Server.Stop();
                UDP_VIDEO_Conference_Server.Stop();
                UDP_AUDIO_Conference_Server.Stop();
                UDP_VIDEO_Conference_Client.Stop();
                UDP_AUDIO_Conference_Client.Stop();
            }
            catch (Exception)
            {

            }
        }
        public void Restart()
        {
            CloseModel();
            //Перезапускаем приложение
            System.Windows.Forms.Application.Restart();
            Environment.Exit(1);
        }

        private void CheckAutoStart()
        {
            string path;
            if (ClickOnceUpdater.IsClickOnce) path = Properties.Settings.Default.StartPath;
            else path = Assembly.GetExecutingAssembly().Location;
            if (IsAutoStartEnable)
            {
                Utilities.AutoStartSettings.SetAutoStart(Properties.Settings.Default.ApplicationName, path);
            }
            else
                if (Utilities.AutoStartSettings.IsAutoStartEnabled(Properties.Settings.Default.ApplicationName, path))
                    Utilities.AutoStartSettings.UnSetAutoStart(Properties.Settings.Default.ApplicationName);
        }

        public void RunConferencService()
        {
            //ThreadPool.QueueUserWorkItem(new WaitCallback(runConferencService));
            NoArgDelegate executor = new NoArgDelegate(this.runConferencService);
            executor.BeginInvoke(null, null);
        }

        private void runConferencService()
        {

            //Создание InstanceContext для обработки сообщений на интерфейсе обратного вызова. 
            //Создается экземпляр ChatApp и передается в InstanceContext.
            instanceContext = new InstanceContext(this);

            //Создание участника с заданной конфигурацией конечной точки
            //Каждый участник открывает дуплексный канал к сетке
            //участник - это экземпляр приложения разговора, открывший канал к сетке
            factory = new DuplexChannelFactory<IConferenceChannel>(instanceContext, "ChatEndpoint");
            participant = factory.CreateChannel();
            participant.AllowOutputBatching = true;
            //Получение PeerNode, связанного с участником, и регистрация событий в интерактивном и автономном режиме
            //PeerNode представляет узел в сетке. Сетка - это именованная коллекция подключенных узлов.
            //ostat = participant.GetProperty<IOnlineStatus>();

            ////TODO: Исправить на лямбда обработку!!!
            //ostat.Offline += (_1, _2) => { IsConnect = false; };
            //ostat.Online += (_1, _2) => { IsConnect = true; };


            bool bIsConnected = false;
            while (!bIsConnected)
            {
                App.Log.Info("Подключение к серверу...");
                Console.WriteLine("Подключение к серверу...");
                try 
	            {
                    //participant = factory.CreateChannel();
                    participant.Open();
                    bIsConnected = true;
                    App.Log.Info("Подключение к серверу успешно!");
                    Console.WriteLine("Подключение к серверу успешно!");
	            }
                catch (CommunicationException ex)
	            {
                    App.Log.Error("Ошибка подключения к серверу!", ex);
                    Console.WriteLine("Ошибка подключения к серверу!");
                    Thread.Sleep(1000);
	            }
            }
            Participant.Join(Room, MachineName, SoftWareVersion, IpAddres);
            Participant.TimeSignalRequest();
        }

        public void CloseConferencService()
        {
            //TODO: Протестировать закрытие. Закрыть, потом опять попытаться открыть...
            participant.Close();
            instanceContext = null;
            factory = null;
            ostat = null;
        }
        #region Sound Hren'
        private short ComplementToSigned(ref byte[] bytArr, int intPos) // 2's complement to normal signed value
        {
            short snd = BitConverter.ToInt16(bytArr, intPos);
            if (snd != 0)
                snd = Convert.ToInt16((~snd | 1));
            return snd;
        }

        private byte[] SignedToComplement(short shtVal) //Convert to 2's complement and return as byte array of 2 bytes
        {
            byte[] bt = new byte[2];
            shtVal = Convert.ToInt16((~shtVal | 1));
            bt = BitConverter.GetBytes(shtVal);
            return bt;
        }
        public class ClassSummAudio
        {
            public byte[] BufB;
            public string From;
        
        }

        private int NummOffSound = 0;
        private string RightNowPlay;
        DateTime SubDT = new DateTime();
        private void Filler(IntPtr data, int size)
        {
            lock (this)
            {
                if (m_PlayBuffer == null || m_PlayBuffer.Length < size) m_PlayBuffer = new byte[size];

                if (bFlagMixVoice && lst_bufSampAudio.Count!=0)
                {
                    bool bFlagIgnorServer = false;
                    DateTime N = DateTime.Now;
                    bFlagIgnorServer = lst_bufSampAudio.Any(p => (N - p.DtRec).TotalMilliseconds < 1500 && p.from != "server");

                    if (bFlagIgnorServer)
                        for (int i = 0; i < lst_bufSampAudio.Count; i++)
                        {

                            //*  
                            if (lst_bufSampAudio[i].from == "server")
                            {
                                lst_bufSampAudio[i].AudioStream.Flush();
                                continue;
                            }
                        }

                    int NumSumm = lst_bufSampAudio.Count(t => t.AudioStream.Length >= size);
                    if (NumSumm != 0)
                    {
                            for (int i = 0; i < lst_bufSampAudio.Count; i++)
                            {
                                if (lst_bufSampAudio[i].AudioStream.Length < size) continue;

                                    lst_bufSampAudio[i].AudioStream.Read(m_PlayBuffer, 0, size);
                                //*   
                                    if (lst_bufSampAudio[i].AudioStream.Length > 10000)
                                        lst_bufSampAudio[i].AudioStream.Flush();//*/
                            }

                    }
                    else if (NumSumm == 0)
                    {
                        for (int i = 0; i < m_PlayBuffer.Length; i++)
                            m_PlayBuffer[i] = 0;
                    }
                    if (lst_bufSampAudio.Count!=0)
                    TestInfo3 = "Rec" + RecByte + "  PlayLength" + lst_bufSampAudio[0].AudioStream.Length;
                }
                else if(bFlagMixVoice==false && mFifo.Length>=size)
                {
                    TestInfo3 = "Rec" + RecByte + "  PlayLength" + mFifo.Length;
                    mFifo.Read(m_PlayBuffer, 0, size);
                    if (mFifo.Length > 10000)
                    {
                        mFifo.Flush();
                    }
                }
                else
                    for (int i = 0; i < m_PlayBuffer.Length; i++)
                        m_PlayBuffer[i] = 0;
                Marshal.Copy(m_PlayBuffer, 0, data, size);
            }
        }

        #endregion

        private bool ContainsConferenceRoom(string from)
        {
            return (ConferenceRoom.Any(p => p.SName == from));
        }
        private void InitControlDevice()
        {
            if (DeviceControl == null) return;
            //Состояние устройства
            DeviceControl.eStatus += (ds) => { DeviceControlStatus = ds; };
            //Результат подключения
            DeviceControl.evStatusConnect += new DeviceExch.ChangeStatus(ControlDevice_evStatusConnect);
            //Нажатие главной кнопки
            DeviceControl.evKeyboard += new DeviceExch.StatusKeyboard(ControlDevice_evKeyboard);
            DeviceControl.IntervalGetStatus = TimeSpan.FromMilliseconds(100); //Твой тайм спан
            DeviceControl.Connect2Device(DeviceExch.TypeDevice.Selector, 0);    //Подключение к устройству
        }

        void ControlDevice_evKeyboard(
            List<DeviceExch.DevStatus.SelectorButton> rButton, 
            List<DeviceExch.DevStatus.SelectorButton> cButton,
            bool CancelButton)
        {
            string room;
            foreach (var r in rButton)
            {
                room = MachineSettings.ButtonNumberToRoom(r.Number);
                if(r.IsActive)
                {
                    if (SelectedSelectorRooms.Contains(room)) continue;
                    SelectedSelectorRooms.Add(room);
                }
                else
                {
                    if (!SelectedSelectorRooms.Contains(room)) continue;
                    SelectedSelectorRooms.Remove(room);
                }
            }

            if(rButton!=null)
            {
                string str = "";
                foreach (var _var in SelectedSelectorRooms)
                {
                    str += _var + " ";
                }
                this.MainDispatcher.BeginInvoke(
                    DispatcherPriority.Normal,
                    (ThreadStart) delegate()
                                      {
                                          TestInfo = str;
                                      });

            }

            foreach (var r in cButton)
            {
                room = MachineSettings.ButtonNumberToRoom(r.Number+30);
                var confList = ConferenceRoom.Single(p => p.SName == room);

                if (r.IsActive)
                {
                    if(confList.CurState==ConferenceClass.State.Initial)
                    {
                        confList.ReqConnect = true;
                    }
                    else if(confList.CurState==ConferenceClass.State.CanAnswer)
                    {
                        confList.IsAnswer = true;
                    }
                }
                else
                {
                    confList.IsNotAnswer = true;
                }
            }

            if(CancelButton)
            {
                foreach (var conf in ConferenceRoom)
                {
                    conf.IsNotAnswer = true;
                }
            }
        }

        void ControlDevice_evStatusConnect(DeviceExch.StatusConnect scStat)
        {
            switch (scStat)
            {
                case DeviceExch.StatusConnect.NotConnect:
                    App.Log.Info("Пульт управления не подключен");
                    break;
                case DeviceExch.StatusConnect.Connected:
                    App.Log.Info("Пульт управления подключен");
                    break;
            }
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #region IConference Members

        public void Join(string userName, string machineName, string softwareVersion, string strIpAddres)
        {
            return;
        }

        public void Leave(string userName)
        {
            return;
        }

        public void PingRequest(string userName, string machineName, string softwareVersion, string strIpAddres)
        {
            return;
        }

        public void TextMessage(string From, List<string> To, string strMessage) {}

        public void VideoSample(SendBytes sb)
        {
            if (sb.From == Room || !sb.To.Contains(Room) || !ContainsConferenceRoom(sb.From)) return;

         //   byte[] btVideo = btVideoSample.Clone() as byte[];

            MainDispatcher.BeginInvoke((Action<byte[]>)((btVideo) => 
            {
                MemoryStream ms = new MemoryStream(btVideo);
                BitmapImage bmp = new BitmapImage();
                bmp.BeginInit();
                bmp.StreamSource = ms;
                bmp.EndInit();

                foreach (var item in ConferenceRoom.Where(p => p.SName == sb.From))
                {
                    ConferenceClass item1 = item;

                    if (item1 != null)
                    {
                        MainDispatcher.BeginInvoke((Action<BitmapImage>)((t) => { item1.ConferenceImage = t; }), bmp);
                    }
                }
            }),
            DispatcherPriority.Send, sb.Data.Clone() as byte[]);

        }



        private class ClassMultiAudio
        {
            public FifoStream AudioStream;
            public string from;
            public int IndexMessage;
            public DateTime DtRec;  //Время последнего приёма звука от источника
            public ClassMultiAudio()
            {
                AudioStream = new FifoStream();
            }
        }

        private int RecByte = 0;
        
        private bool bFlagMixVoice = true;
        FifoStream mFifo = new FifoStream();
        private List<ClassMultiAudio> lst_bufSampAudio = new List<ClassMultiAudio>();

        

        public void AudioSample(SendBytes sb)
        {
            if (sb.From == Room) return;
            if (sb.To.Contains(Room) == false && sb.From != "server") return;

            var AudioSample = VoiceCapturedEventArgs.Deserialise(sb.Data.Clone() as byte[]);
            
            GC.Collect();

            if (sb.From == ServerName)
            {
                if (!IsIpAudioRecived) return;
            }
            lock (this)
            {
                this.MainDispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                (ThreadStart)delegate()
                {
                    if (sb.From != null)
                    {
                        string str = sb.To.Aggregate("", (current, var) => current + (var + " "));
                        //TestInfo2 = "thisR =" + Room + "    audio R:" + From + "  To " + str;
                    }
                    else
                    {
                       // TestInfo2 = "";
                    }
                });

                if(bFlagMixVoice)
                {
                    bool bFlag = false;

                    foreach (ClassMultiAudio t in lst_bufSampAudio.Where(t => t.from == sb.From))
                    {
                        if (t.IndexMessage > AudioSample.IndexMessage)
                        {
                            continue;
                        }
                        t.DtRec = DateTime.Now;
                        t.IndexMessage = AudioSample.IndexMessage;
                        t.AudioStream.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                        bFlag = true;
                    }
                       
                    if (bFlag == false)
                    {
                        var cl = new ClassMultiAudio()
                                     {
                                         from = sb.From,
                                         IndexMessage = AudioSample.IndexMessage,
                                         DtRec = DateTime.Now
                                     };
                        cl.AudioStream.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                        lst_bufSampAudio.Add(cl);

                    }   
                }
                else mFifo.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                RecByte += AudioSample.Source.Length;


            }
        }

        private int IndexAudioMessage;
        private int IndexVideoMessage;
        private int MissAudioMessage;
        private int MissVideoMessage;

        public void AudioIPSample(SendBytes sb)
        {
         //   return;
            if (ServiceTime - sb.TimeStamp > TimeSpan.FromMilliseconds(2000))
            {
                MissAudioMessage++;
                OnPropertyChanged("TestInfo2");

                return;
            }
            IndexAudioMessage = sb.IndexMessage;

            GC.Collect();
            var AudioSample = VoiceCapturedEventArgs.Deserialise(sb.Data.Clone() as byte[]);

            if (sb.From == ServerName)
            {
                if (!IsIpAudioRecived) return;
            }

            lock (lst_bufSampAudio)
            {
                if (bFlagMixVoice)
                {
                    bool bFlag = false;
                    foreach (ClassMultiAudio t in lst_bufSampAudio.Where(t => t.from == sb.From))
                    {
                        if (t.IndexMessage > AudioSample.IndexMessage)
                        {
                            continue;
                        }

                        t.IndexMessage = AudioSample.IndexMessage;
                        t.DtRec = DateTime.Now;
                        t.AudioStream.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                        bFlag = true;
                    }

                    if (bFlag == false)
                    {
                        var cl = new ClassMultiAudio()
                        {
                            from = sb.From,
                            IndexMessage = AudioSample.IndexMessage
                        };
                        cl.DtRec = DateTime.Now;
                        cl.AudioStream.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                        lst_bufSampAudio.Add(cl);

                    }
                }
                else mFifo.Write(AudioSample.Source, 0, AudioSample.Source.Length);
                RecByte += AudioSample.Source.Length;


            }

        //    throw new NotImplementedException();
        }

        public void Ping()
        {
            if (Participant != null && Participant.State == CommunicationState.Opened) 
                Participant.PingRequest(Room, MachineName, SoftWareVersion, IpAddres);
        }

        public void ActiveClientList(List<ClientInfo> clientList)
        {
            //var t2 = clientList.Where(p => p.UserName != ServerName && p.UserName != UserName).ToList();
           /* var t2 = clientList.Where(p => p.UserName != ServerName).ToList();

            foreach (var item in t2)
            {
                if (!ClientList.Where(p => p.UserName == item.UserName).Any())
                    ClientList.Add(item);
            }

            var t = ClientList.ToList();
            foreach (var item in t)
            {
                if (!clientList.Where(p => p.UserName == item.UserName).Any()) 
                    ClientList.Remove(item);
            }*/
        }

        public void TimeSignal(string From, DateTime Time)
        {
            if (From != ServerName) return;
            ServiceDeltaTime = DateTime.Now - Time;
        }

        public void TimeSignalRequest() {}


        private DateTime DT_VideoFpsCalc;
        private int numFrame = 0;
        public void VideoIPSample(SendBytes message)
        {
            if (message.From != ServerName || !IsIpVideoRecived) return;
            if (/*message.IndexMessage < IndexVideoMessage || */ServiceTime - message.TimeStamp > TimeSpan.FromMilliseconds(2000))
            {
                MissVideoMessage++;
                OnPropertyChanged("TestInfo2");

                return;
            }
            IndexVideoMessage = message.IndexMessage;

            MainDispatcher.Invoke((Action<byte[]>)(GetIpVideo), DispatcherPriority.Send, message.Data.Clone() as byte[]);
        }

        private byte[] BufLight;
        public void DeviceControlLights(string From, byte[] btLights)
        {
            if (From == Room) return;
            if (BufLight == null)
                BufLight = btLights;
            else
            {
                bool bFlagChange = false;
                for(int i=0;i<btLights.Length;i++)
                {
                    if (BufLight[i] != btLights[i])
                        bFlagChange = true;
                    BufLight[i] = btLights[i];
                }
                if(bFlagChange==false) return;
            }
            if(DeviceControl.StatCon == DeviceExch.StatusConnect.Connected)
                DeviceControl.TaskCommand(SelectorDevice.DeviceExch.CommandName.LightSend, BufLight);
        }

        public void CancelCall(string From, string To)
        {
            if (ClientType != ClientTypes.Extended) return;
            if (To != Room) return;

            foreach (var cr in ConferenceRoom.Where(p => p.SName == To))
            {
                if (SelectedConferenceRoom.Contains(To) == true)
                    SelectedConferenceRoom.Remove(To);
                cr.CurState = ConferenceClass.State.Initial;
            }
        }

        public void StartConference(string From, string To)
        {
            if (ClientType != ClientTypes.Extended) return;
            if (To != Room) return;

            foreach (var cr in ConferenceRoom.Where(p => p.SName == From))
            {
                if (selectedConferenceRoom.Contains(From) == false)
                    SelectedConferenceRoom.Add(From);
                cr.CurState = ConferenceClass.State.Work;
            }
        }

        public void StopConference(string From, string To)
        {
            if (ClientType != ClientTypes.Extended) return;
            if (To != Room) return;

            foreach (var cr in ConferenceRoom.Where(p => p.SName == From))
            {
                if (SelectedConferenceRoom.Contains(From) == true)
                    SelectedConferenceRoom.Remove(From);
                cr.CurState = ConferenceClass.State.Initial;
            }

        }
        
        #endregion //IConference Members

        #region IConference Members

        public void BeginCall(string From, string To)
        {
            if (ClientType != ClientTypes.Extended) return;
            if(To!=Room) return;

            foreach (var cr in ConferenceRoom.Where(p => p.SName == From))
            {
                cr.CurState = ConferenceClass.State.CanAnswer;
                cr.WhoTalk = From;
            }

          //  throw new NotImplementedException();
        }

        #endregion
    }
}
